#pragma once
#include <Windows.h>

#include <atomic>
#include <cassert>
#include <string>


namespace multiprocess {
namespace sync {
template <size_t MaxCount>
class semaphore {
 public:
  semaphore(size_t sem_key, size_t initial_count) {
    create(sem_key, initial_count);
  };
  ~semaphore() {
    if (m_handle_of_sem) CloseHandle(m_handle_of_sem);
  }

  void release(int num = 1) {
    assert(m_handle_of_sem);
    assert(ReleaseSemaphore(m_handle_of_sem, num, NULL));
  }
  void acquire() {
    assert(m_handle_of_sem);
    assert(WaitForSingleObject(m_handle_of_sem, INFINITE) == WAIT_OBJECT_0);
  }
  bool try_acquire() {
    assert(m_handle_of_sem);
    return WaitForSingleObject(m_handle_of_sem, 0L) == WAIT_OBJECT_0;
  }
  bool try_acquire_for(size_t time) {
    assert(m_handle_of_sem);
    return WaitForSingleObject(m_handle_of_sem, time) == WAIT_OBJECT_0;
  }
  size_t max_count() const { return MaxCount; }

 private:
  bool create(size_t sem_key, size_t initial_count) {
    m_handle_of_sem = CreateSemaphore(NULL, initial_count, MaxCount,
                                      ::std::to_wstring(sem_key).data());
    assert(m_handle_of_sem);
    return GetLastError() != ERROR_ALREADY_EXISTS;
  }
  bool open(size_t sem_key) {
    if (m_handle_of_sem) CloseHandle(m_handle_of_sem);
    m_handle_of_sem = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE,
                                    ::std::to_wstring(sem_key).data());
    return m_handle_of_sem != NULL;
  }
  HANDLE m_handle_of_sem;
};
}  // namespace sync
}  // namespace multiprocess